package com.cml.getway.core.netty.processor.filter.route;

import com.cml.getway.common.config.Rule;
import com.cml.getway.common.constants.ProcessorFilterConstants;
import com.cml.getway.common.enums.ResponseCode;
import com.cml.getway.common.exception.GetwayConnectException;
import com.cml.getway.common.exception.GetwayResponseException;
import com.cml.getway.common.util.TimeUtil;
import com.cml.getway.core.GetwayConfigLoader;
import com.cml.getway.core.context.Context;
import com.cml.getway.core.context.GetwayContext;
import com.cml.getway.core.context.GetwayResponse;
import com.cml.getway.core.helper.AsyncHttpHelper;
import com.cml.getway.core.netty.processor.filter.AbstractEntryProcessorFilter;
import com.cml.getway.core.netty.processor.filter.Filter;
import com.cml.getway.core.netty.processor.filter.ProcessorFilterType;
import lombok.extern.slf4j.Slf4j;
import org.asynchttpclient.Request;
import org.asynchttpclient.Response;

import java.util.concurrent.CompletableFuture;


@Filter(
		id = ProcessorFilterConstants.HTTP_ROUTE_FILTER_ID,
		name = ProcessorFilterConstants.HTTP_ROUTE_FILTER_NAME,
		value = ProcessorFilterType.ROUTE,
		order = ProcessorFilterConstants.HTTP_ROUTE_FILTER_ORDER
		)
@Slf4j
public class HttpRouteFilter extends AbstractEntryProcessorFilter<Rule.FilterConfig> {

	public HttpRouteFilter() {
		super(Rule.FilterConfig.class);
	}

	@Override
	public void entry(Context ctx, Object... args) throws Throwable {
		GetwayContext getwayContext = (GetwayContext)ctx;
		Request request = getwayContext.getRequestMutale().build();
		
		//	设置RS:
		getwayContext.setRSTime(TimeUtil.currentTimeMillis());
		
		CompletableFuture<Response> future = AsyncHttpHelper.getInstance().executeRequest(request);
		
		//	双异步和单异步模式
		boolean whenComplete = GetwayConfigLoader.getGetwayConfig().isWhenComplete();
		
		//	单异步模式
		if(whenComplete) {
			future.whenComplete((response, throwable) -> {
				complete(request, response, throwable, getwayContext, args);
			});
		}
		//	双异步模式
		else {
			future.whenCompleteAsync((response, throwable) -> {
				complete(request, response, throwable, getwayContext, args);
			});					
		}
	}

	private void complete(Request request, 
			Response response,
			Throwable throwable,
			GetwayContext getwayContext,
			Object... args) {
		try {
			//	设置RR:
			getwayContext.setRRTime(TimeUtil.currentTimeMillis());
			
			//	1. 释放请求资源
			getwayContext.releaseRequest();
			//	2. 判断是否有异常产生
			if(java.util.Objects.nonNull(throwable)) {
				String url = request.getUrl();
				//	超时异常
				if(throwable instanceof java.util.concurrent.TimeoutException) {
					log.warn("#HttpRouteFilter# complete返回响应执行， 请求路径：{}，耗时超过 {}  ms.",
							url, 
							(request.getRequestTimeout() == 0 ?
									GetwayConfigLoader.getGetwayConfig().getHttpRequestTimeout() :
									request.getRequestTimeout())
							);
					//	网关里设置异常都是使用自定义异常
					getwayContext.setThrowable(new GetwayResponseException(ResponseCode.REQUEST_TIMEOUT));
				} 
				//	其他异常情况
				else {
					getwayContext.setThrowable(new GetwayConnectException(throwable,
							getwayContext.getUniqueId(),
							url,
							ResponseCode.HTTP_RESPONSE_ERROR));
				}
			} 
			//	正常返回响应结果：
			else {
				//	设置响应信息
				getwayContext.setResponse(GetwayResponse.buildRapidResponse(response));
			}
			
		} catch (Throwable t) {
			//	最终兜底异常处理
			getwayContext.setThrowable(new GetwayResponseException(ResponseCode.INTERNAL_ERROR));
			log.error("#HttpRouteFilter# complete catch到未知异常", t);
		} finally {
			try {
				//	1.	设置写回标记
				getwayContext.writtened();
				
				//	2. 	让异步线程内部自己进行触发下一个节点执行
				super.fireNext(getwayContext, args);
			} catch (Throwable t) {
				//	兜底处理，把异常信息放入上下文
				getwayContext.setThrowable(new GetwayResponseException(ResponseCode.INTERNAL_ERROR));
				log.error("#HttpRouteFilter# fireNext出现异常", t);
			}
		}
	}

}
